﻿/*  
	------------------------------------------------------------------------
	 NQ Software Development Kit Component
	
	 Homepage: http://www.awzhome.de/
	------------------------------------------------------------------------
	
	This Source Code Form is subject to the terms of the Mozilla Public
	License, v. 2.0. If a copy of the MPL was not distributed with this
	file, You can obtain one at http://mozilla.org/MPL/2.0/.
   
	The Original Code is code of NQ Software Development Kit Component.

	The Initial Developer of the Original Code is Andreas Weizel.
	Portions created by the Initial Developer are
	Copyright (C) 2012 Andreas Weizel. All Rights Reserved.
	
	Contributor(s): (none)
	
	------------------------------------------------------------------------
*/

using System;
using System.Text;
using AWZhome.NQ.Core;
using AWZhome.NQ.CommonServices;
using NQCSServices = AWZhome.NQ.CommonServices.Services;
using NQsdkServices = AWZhome.NQsdk.Services;
using AWZhome.NQsdk.Visual;
using AWZhome.NQ.Core.Fluent;
using AWZhome.NQsdk.Helpers;
using System.Collections.ObjectModel;
using System.Globalization;
using AWZhome.NQsdk.Interfaces;
using System.IO;
using System.Reflection;
using AWZhome.NQ.CommonServices.Fluent;
using Microsoft.Win32;

namespace AWZhome.NQsdk.Services
{
	[NQExportedService]
	public class LogMessageViewService : ViewServiceBase, NQsdkServices.ILogMessageViewService
	{
		private LogMessageView _logMessageView;
		private INQResourceGUIManager _servRes;
		private IBarManager _servBar;
		private string _displayName;
		private NQLogType _levelFilter;
		private bool _autoScroll;
		private bool _viewChanged;

		private ObservableCollection<LogMessage> _logMessages = new ObservableCollection<LogMessage>();

		public LogMessageViewService(ISession sessionService)
			: base(sessionService)
		{
		}

		protected override bool CheckSessionType(ISession session)
		{
			return true;
		}

		public override string DisplayName
		{
			get
			{
				return _displayName;
			}
		}

		public override bool IsChanged
		{
			get
			{
				return _viewChanged;
			}
		}

		public override bool CanBeClosed
		{
			get
			{
				return true;
			}
		}

		public override bool IsLoggable
		{
			get
			{
				return false;
			}
		}

		public override bool IsDocument
		{
			get
			{
				return true;
			}
		}

		public override object DisplayedObject
		{
			get
			{
				return _logMessageView;
			}
		}

		public override System.Windows.Media.ImageSource Icon
		{
			get
			{
				return null;
			}
		}

		public override ViewDisplayMode DockingType
		{
			get
			{
				return ViewDisplayMode.All;
			}
		}

		public override string ToolTip
		{
			get
			{
				return this.DisplayName;
			}
		}

		public override void Initialize(string context)
		{
			base.Initialize(context);

			// Get resource service
			_servRes = ServiceCore
				.Get<NQCSServices.IResourceGUIService>()
				.To;
			_displayName = _servRes.GetResString("NQsdk", "LogMessageView.DisplayName");

			// Create visual view control
			_logMessageView = new LogMessageView();
			_logMessageView.DataContext = _logMessages;

			_servBar = _logMessageView.ToolbarHost.BarService;
			_servBar.ItemClicked += new EventHandler<BarItemClickedEventArgs>(_servBar_ItemClicked);
			_servBar.DockBottom = false;
			_servBar.DockTop = true;
			_servBar.DockLeft = false;
			_servBar.DockRight = false;
			Stream xmlstream = Assembly.GetExecutingAssembly().GetManifestResourceStream("AWZhome.NQsdk.menudef.SDKLogViewerToolbar.xml");
			_servBar.CreateItemsFromXml(xmlstream);
			xmlstream.Close();
			_servBar.LoadBarSettings();

			// Always activate the toolbar (settings should take no effect)
			_servBar.GetBand("SDKLogViewerToolbar").BandVisible = true;

			// Load filter configuration instead of activating all levels
			_levelFilter = (NQLogType) Configuration
				.Value("logViewerLevelFilter")
				.OfComponents()
				.InGroup("NQsdk")
				.Get.AsInt32((int) NQLogType.All);
			_servBar.GetItem("SDKLogViewerToolbar.FilterDebug").ItemChecked = ((_levelFilter & NQLogType.Debug) == NQLogType.Debug);
			_servBar.GetItem("SDKLogViewerToolbar.FilterInfo").ItemChecked = ((_levelFilter & NQLogType.Info) == NQLogType.Info);
			_servBar.GetItem("SDKLogViewerToolbar.FilterWarning").ItemChecked = ((_levelFilter & NQLogType.Warning) == NQLogType.Warning);
			_servBar.GetItem("SDKLogViewerToolbar.FilterError").ItemChecked = ((_levelFilter & NQLogType.Error) == NQLogType.Error);

			// Load auto-scroll setting
			_autoScroll = Configuration
				.Value("logViewerAutoScroll")
				.OfComponents()
				.InGroup("NQsdk")
				.Get.AsBoolean(false);
			_servBar.GetItem("SDKLogViewerToolbar.AutoScroll").ItemChecked = _autoScroll;

			// Load last search string
			IBarItem searchTextBox = _servBar.GetItem("SDKLogViewerToolbar.SearchText");
			searchTextBox.ItemText = Configuration
				.Value("logViewerLastSearch")
				.OfComponents()
				.InGroup("NQsdk")
				.Get.AsString("");

			// Register handler for log messages
			NQServiceManager.Instance.MessageLogged += new NQLogEventHandler(NQServiceManager_MessageLogged);

			// First get all messages from cache
			ISdkLogCacheService logCacheService = ServiceCore
				.Get<ISdkLogCacheService>()
				.To;
			foreach (var logMessage in logCacheService.FetchLogMessages())
			{
				if ((_levelFilter & logMessage.Level) == logMessage.Level)
				{
					_logMessages.Add(logMessage);
				}
			}
			this.ScrollToLastMessage();
			logCacheService.RegisterViewer();
		}

		public override bool RequestClosing()
		{
			// Unregister event handler
			NQServiceManager.Instance.MessageLogged -= NQServiceManager_MessageLogged;

			ISdkLogCacheService logCacheService = ServiceCore
				.Get<ISdkLogCacheService>()
				.To;
			logCacheService.UnregisterViewer();

			return true;
		}

		public override void LoadContents()
		{
			base.LoadContents();
		}

		public override void SaveContents(ViewSaveReason saveReason)
		{
			base.SaveContents(saveReason);
		}

		public override void Activate(IView oldView)
		{
			base.Activate(oldView);

			this.ScrollToLastMessage();
			_viewChanged = false;
			this.ViewManager.UpdateView(this, true);
		}

		private void NQServiceManager_MessageLogged(object sender, NQLogEventArgs e)
		{
			if ((_levelFilter & e.LogType) == e.LogType)
			{
				UInt64 messageID = 0;
				if (_logMessages.Count > 0)
				{
					messageID = _logMessages[_logMessages.Count - 1].MessageID;
				}
				messageID++;

				// Add new LogMessage item
				String sourceService = "";
				if (e.SourceService != null)
				{
					INQServiceInfo serviceInfo = ServiceCore.ObjectMetaInfo(e.SourceService.GetType());
					if (serviceInfo != null)
					{
						sourceService = serviceInfo.ServiceType.ToString();
					}
				}
				LogMessage message = new LogMessage()
				{
					MessageID = messageID,
					Level = e.LogType,
					Timestamp = e.Timestamp.ToShortDateString() + " " + e.Timestamp.ToString("HH:mm:ss.fff", CultureInfo.InvariantCulture),
					SourceService = sourceService,
					Message = e.LogMessage
				};

				_logMessages.Add(message);
				this.ScrollToLastMessage();

				// "Flash" this tab
				_viewChanged = (this.ViewManager.ActiveView != this);
				this.ViewManager.UpdateView(this, false);
			}
		}

		private void _servBar_ItemClicked(object sender, BarItemClickedEventArgs e)
		{
			switch (e.Item.ItemKey)
			{
				case "SDKLogViewerToolbar.Save":
					// Allow the user to save the current log contents
					this.SaveCurrentLog();
					break;

				case "SDKLogViewerToolbar.FilterDebug":
					// Set or unset the filter for Debug level
					if (e.Item.ItemChecked)
					{
						_levelFilter = _levelFilter | NQLogType.Debug;
					}
					else
					{
						_levelFilter = _levelFilter & ~NQLogType.Debug;
					}
					break;

				case "SDKLogViewerToolbar.FilterInfo":
					// Set or unset the filter for Info level
					if (e.Item.ItemChecked)
					{
						_levelFilter = _levelFilter | NQLogType.Info;
					}
					else
					{
						_levelFilter = _levelFilter & ~NQLogType.Info;
					}
					break;

				case "SDKLogViewerToolbar.FilterWarning":
					// Set or unset the filter for Warning level
					if (e.Item.ItemChecked)
					{
						_levelFilter = _levelFilter | NQLogType.Warning;
					}
					else
					{
						_levelFilter = _levelFilter & ~NQLogType.Warning;
					}
					break;

				case "SDKLogViewerToolbar.FilterError":
					// Set or unset the filter for Error level
					if (e.Item.ItemChecked)
					{
						_levelFilter = _levelFilter | NQLogType.Error;
					}
					else
					{
						_levelFilter = _levelFilter & ~NQLogType.Error;
					}
					break;

				case "SDKLogViewerToolbar.AutoScroll":
					// Set or unset auto-scroll feature
					_autoScroll = e.Item.ItemChecked;
					this.ScrollToLastMessage();
					break;

				case "SDKLogViewerToolbar.FindPrevious":
					// Search backwards
					this.FindLog(false);
					break;

				case "SDKLogViewerToolbar.FindNext":
					// Search forward
					this.FindLog(true);
					break;

				default:
					break;
			}

			// Save current level filter & auto-scroll setting
			Configuration
				.Value("logViewerLevelFilter")
				.OfComponents()
				.InGroup("NQsdk")
				.Set(new ConfigData().FromInt32((int) _levelFilter));
			Configuration
				.Value("logViewerAutoScroll")
				.OfComponents()
				.InGroup("NQsdk")
				.Set(new ConfigData().FromBoolean(_autoScroll));
		}

		/// <summary>
		/// Searches for text given by user in toolbar and selects next matching log message.
		/// </summary>
		/// <param name="forward">If <c>true</c> searches in forward direction, backwards otherwise.</param>
		private void FindLog(bool forward)
		{
			IBarItem searchTextBox = _servBar.GetItem("SDKLogViewerToolbar.SearchText");
			string searchText = searchTextBox.ItemText.ToLower();
			if (String.IsNullOrEmpty(searchText))
			{
				// No search text, exit here
				return;
			}

			// Save the last search
			Configuration
				.Value("logViewerLastSearch")
				.OfComponents()
				.InGroup("NQsdk")
				.Set(new ConfigData().FromString(searchTextBox.ItemText));

			int startIndex = _logMessageView.LogMessagesListView.SelectedIndex;
			if ((startIndex == -1) && (_logMessages.Count > 0))
			{
				startIndex = 0;
			}
			else
			{
				// Start from previous/next item
				if (forward)
				{
					startIndex++;
				}
				else
				{
					startIndex--;
				}
			}
			if (startIndex > -1)
			{
				LogMessage logMessageItem = null;
				if (forward)
				{
					for (int i = startIndex; i < _logMessages.Count; i++)
					{
						logMessageItem = _logMessages[i];
						if ((logMessageItem != null) && (logMessageItem.Message.ToLower().Contains(searchText)))
						{
							// Found an item containing search text
							_logMessageView.LogMessagesListView.SelectedItem = logMessageItem;
							_logMessageView.LogMessagesListView.ScrollIntoView(logMessageItem);
							break;
						}
					}
				}
				else
				{
					for (int i = startIndex; i >= 0; i--)
					{
						logMessageItem = _logMessages[i];
						if ((logMessageItem != null) && (logMessageItem.Message.ToLower().Contains(searchText)))
						{
							// Found an item containing search text
							_logMessageView.LogMessagesListView.SelectedItem = logMessageItem;
							_logMessageView.LogMessagesListView.ScrollIntoView(logMessageItem);
							break;
						}
					}
				}
			}
		}

		private void SaveCurrentLog()
		{
			SaveFileDialog saveFileDialog = new SaveFileDialog();
			saveFileDialog.Filter = _servRes.GetResString("NQsdk", "LogMessageView.FileFilterLog") + "|*.log|"
				+ _servRes.GetResString("NQCS", "FileFilter.AllFiles") + "|*.*";
			saveFileDialog.AddExtension = true;
			if (saveFileDialog.ShowDialog().Value)
			{
				// Write all current files
				using (StreamWriter write = new StreamWriter(saveFileDialog.FileName, false, Encoding.UTF8))
				{
					foreach (LogMessage message in _logMessages)
					{
						string logSource = message.SourceService;
						string typetext = String.Empty;

						// Check if the log event should be processed
						switch (message.Level)
						{
							case NQLogType.Debug:
								typetext = "debug";
								break;
							case NQLogType.Error:
								typetext = "ERROR";
								break;
							case NQLogType.Warning:
								typetext = "WARN";
								break;
							case NQLogType.Info:
								typetext = "info";
								break;
						}

						logSource = String.IsNullOrEmpty(logSource) ? "<Base code>" : logSource;
						write.WriteLine("[{0}] [{1}] [{2}] {3}", message.Timestamp, logSource, typetext, message.Message);
					}
					write.Flush();
					write.Close();
				}
			}
		}

		private void ScrollToLastMessage()
		{
			// If auto-scroll setting is active, scroll to last message
			if (_autoScroll && (_logMessages.Count > 0))
			{
				_logMessageView.LogMessagesListView.ScrollIntoView(_logMessages[_logMessages.Count - 1]);
			}
		}
	}
}